﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Script.Serialization;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;
using Fare;

namespace LoadController
{
    public class InvokeHelper
    {
        private static Random rnd = new Random();

        /// <summary>
        ///returns content of textboxes and dropdownlists as a dictionary where key is control ID
        ///text of a textbox is stored in a string
        ///items of a ddl are stored in a string array, where selected value will be first
        /// </summary>
        /// <param name="controls"></param>
        /// <returns></returns>
        public static Dictionary<string, object> GetContentListFromControls(List<Control> controls)
        {
            Dictionary<string, object> stringList = new Dictionary<string, object>();

            controls = controls.Where
                    (control => (control is System.Web.UI.WebControls.TextBox) ||
                                (control is System.Web.UI.WebControls.DropDownList)).ToList();

            foreach (Control c in controls)
            {
                if (c is System.Web.UI.WebControls.TextBox)
                {
                    string text = (((TextBox)c).Text);
                    string id = (((TextBox)c).ID);
                    stringList.Add(id, text);
                }
                else
                {
                    DropDownList curddl = (DropDownList)c;
                    string[] strArr = new string[curddl.Items.Count];
                    string id = curddl.ID;

                    for (int f = 0; f < curddl.Items.Count; f++)
                        strArr[f] = curddl.Items[f].Value;
                    
                    //change selected value with position 1
                    if (curddl.SelectedIndex != 0)
                    {
                        string posOne = strArr[0];

                        strArr[0] = curddl.SelectedValue;
                        strArr[curddl.SelectedIndex] = posOne;
                    }

                    stringList.Add(id, strArr);
                }
            }
            return stringList;
        }

        public static void MapSingleInvoke(Dictionary<string, object> contentOfControls, ref List<object> parameterListForInvoke, Dictionary<string, int> arrayValueDictionary)
        {
            InvokeHelper.Map(contentOfControls, ref parameterListForInvoke, arrayValueDictionary, false);
        }

        public static void MapRandomInvoke(Dictionary<string, object> contentOfControls, ref List<object> parameterListForInvoke)
        {
            InvokeHelper.Map(contentOfControls, ref parameterListForInvoke, null, true);
        }

        private static void Map(Dictionary<string, object> contentOfControls, ref List<object> parameterListForInvoke, Dictionary<string, int> arrayValueDictionary, bool IsRandomInvoke)
        {
            //List<Control> valueControls = null;

            //if (!IsRandomInvoke)
            //{
            //    valueControls = curControls.Where
            //        (control => (control is System.Web.UI.WebControls.TextBox) ||
            //                    (control is System.Web.UI.WebControls.DropDownList)).ToList();
            //}
            //else
            //{
            //    valueControls = curControlsRandom.Where
            //        (control => (control is System.Web.UI.WebControls.TextBox) ||
            //                    (control is System.Web.UI.WebControls.DropDownList)).ToList();
            //}

            int i;
            int k = 0;
            for (i = 0; i < parameterListForInvoke.Count; i++)
            {
                Type t = parameterListForInvoke[i].GetType();

                object obj = null;
                InternalRecursiveMap(t, contentOfControls, ref obj, ref k, arrayValueDictionary, IsRandomInvoke);
                parameterListForInvoke[i] = obj;
            }
        }

        private static void InternalRecursiveMap(Type t, Dictionary<string, object> strings, ref object parameter, ref int k, Dictionary<string, int> arrayValueDictionary, bool IsRandomInvoke)
        {
            if (t == typeof(string))
            {
                //it must be a TextBox
                //TextBox c = (TextBox)valueControls[k];
                parameter = EvaluateString((string)strings.ElementAt(k).Value, IsRandomInvoke);
                k++;
            }
            else if (t.IsArray)
            {
                Type arrayType = t.GetElementType();

                if (!IsRandomInvoke)
                {
                    //string arrayName = valueControls[k].ID.Split(new char[] { '_' })[1];
                    string arrayName = strings.ElementAt(k).Key.Split(new char[] { '_' })[1];

                    KeyValuePair<string, int> correspondingKVP = arrayValueDictionary.Where
                        (kvp => kvp.Key == arrayName).SingleOrDefault();

                    if (!String.IsNullOrEmpty(correspondingKVP.Key))
                    {
                        ArrayList val = new ArrayList(correspondingKVP.Value);
                        int j;
                        for (j = 0; j < correspondingKVP.Value; j++)
                        {
                            //val.Add(Convert.ChangeType(((TextBox)valueControls[k + j]).Text, arrayType));
                            val.Add(Convert.ChangeType((string)strings.ElementAt(k + j).Value, arrayType));
                        }
                        k += j - 1;
                        parameter = val.ToArray(arrayType);
                    }
                }
                else
                {
                    //TextBox tbArrayContent = (TextBox)valueControls[k++];
                    //TextBox tbArraySize = (TextBox)valueControls[k];
                    string tbArrayContent = (string)strings.ElementAt(k++).Value;
                    string tbArraySize = (string)strings.ElementAt(k).Value;
                    int ArraySize;
                    int rndArraySize;

                    if (int.TryParse(tbArraySize, out ArraySize))
                        rndArraySize = rnd.Next(1, ArraySize + 1);
                    else
                        rndArraySize = rnd.Next(1, 11);

                    ArrayList val = new ArrayList(rndArraySize);

                    for (int e = 0; e < rndArraySize; e++)
                    {
                        if (arrayType == typeof(string))
                            val.Add(EvaluateString(tbArrayContent, IsRandomInvoke));
                        else
                            val.Add(EvaluateNumber(tbArrayContent, arrayType, IsRandomInvoke));
                    }

                    parameter = val.ToArray(arrayType);
                }
                k++;
            }
            else if (t.IsEnum)
            {
                //DropDownList c = (DropDownList)valueControls[k];
                
                object[] items = (object[])strings.ElementAt(k).Value;

                //normal invoke
                if (!IsRandomInvoke)
                {
                    //parameter = System.Enum.Parse(t, c.SelectedValue);
                    parameter = System.Enum.Parse(t, (string)items[0]);
                }
                else
                {
                    //if Random is selected, cut "Random" out
                    if (items[0].Equals("Random"))
                    {
                        string[] val = new string[items.Length - 1];

                        for (int f = 1; f < items.Length; f++)
                        {
                            val[f - 1] = (string)items[f];
                        }

                        parameter = System.Enum.Parse(t, val[rnd.Next(0, items.Length - 1)]);
                    }
                    else
                    {
                        parameter = System.Enum.Parse(t, (string)items[0]);
                    }
                }
                k++;
            }
            else if (t == typeof(bool))
            {
                //DropDownList c = (DropDownList)valueControls[k];
                object[] items = (object[])strings.ElementAt(k).Value;
                
                if (!IsRandomInvoke)
                    parameter = Convert.ChangeType(items[0], t);
                else
                {
                    if (items[0].Equals("Random"))
                    {
                        int rnd1 = rnd.Next(0, 2);
                        if (rnd1 == 0)
                            parameter = false;
                        else
                            parameter = true;
                    }
                    else
                        parameter = Convert.ChangeType(items[0], t);
                }
                k++;
            }
            else if (t.IsClass)
            {
                object instClass = CreateNewInstance(t);

                foreach (FieldInfo fi in t.GetFields())
                {
                    object obj = null;
                    InternalRecursiveMap(fi.FieldType, strings, ref obj, ref k, arrayValueDictionary, IsRandomInvoke);
                    instClass.GetType().GetField(fi.Name).SetValue(instClass, obj);
                }
                parameter = instClass;
            }
            else
            {
                //it must be a TextBox
                //TextBox c = (TextBox)valueControls[k];
                parameter = EvaluateNumber((string)strings.ElementAt(k).Value, t, IsRandomInvoke);
                k++;
            }
        }

        private static string EvaluateString(string content, bool IsRandomInvoke)
        {
            if (!IsRandomInvoke)
                return content;
            else
            {
                //handle regular expression
                if (content.StartsWith("re:"))
                {
                    content = content.Remove(0, 3);
                    string rndcomRegEx = GeneratedTextIsCorrect(content);
                    return rndcomRegEx;
                }
                else
                {
                    string[] splits = content.Split(new char[] { ';' });

                    if (splits.Last().Equals(String.Empty))
                        Array.Resize<string>(ref splits, splits.Count() - 1);

                    if (splits.Length > 0)
                    {
                        int rnd2 = rnd.Next(0, splits.Count());

                        return splits[rnd2];
                    }
                    else
                        return String.Empty;
                }
            }
        }

        private static object EvaluateNumber(string content, Type t, bool IsRandomInvoke)
        {
            //single invoke
            if (!IsRandomInvoke)
            {
                try
                {
                    return Convert.ChangeType(content, t);
                }
                catch (Exception ex)
                {
                    throw new Exception("not a valid number specified");    
                }
            }
            else
            {
                //handle regular expression
                if (content.StartsWith("re:"))
                {
                    content = content.Remove(0, 3);
                    //generate random combination from regular expression
                    string rndcomRegEx = GeneratedTextIsCorrect(content);
                    return Convert.ChangeType(rndcomRegEx, t);
                }
                else
                {
                    //generate random combination from pattern: 1-100;200-300;401;402;403
                    if (content.Equals(String.Empty))
                    {
                        if (t == typeof(int))
                        {
                            return rnd.Next(int.MinValue, int.MaxValue);
                        }
                        else if (t == typeof(Int16))
                        {
                            return (Int16)rnd.Next(Int16.MinValue, Int16.MaxValue);
                        }
                        else if (t == typeof(Int32))
                        {
                            return (Int32)rnd.Next(Int32.MinValue, Int32.MaxValue);
                        }
                        else if (t == typeof(Int64))
                        {
                            //parameter = rnd.Next(Int64.MinValue, Int64.MaxValue);
                            return 0;
                        }
                        else if (t == typeof(double))
                        {
                            return 0.0;
                        }
                        else
                        {
                            throw new Exception("Not supported type: " + t.ToString());
                        }
                    }
                    else
                    {
                        string[] rangeSplits = null;
                        if (content.Contains(';'))
                        {
                            rangeSplits = content.Split(new char[] { ';' });

                            if (rangeSplits.Last().Equals(String.Empty))
                                Array.Resize<string>(ref rangeSplits, rangeSplits.Count() - 1);
                        }
                        else
                            rangeSplits = new string[] { content };

                        int rnd2 = rnd.Next(0, rangeSplits.Count());
                        string val = rangeSplits[rnd2];

                        if (val.Contains('-'))
                        {
                            string[] rangeSplits2 = val.Split(new char[] { '-' });
                            if (rangeSplits2.Count() == 2)
                            {
                                int min = int.Parse(rangeSplits2[0]);
                                int max = int.Parse(rangeSplits2[1]);

                                return Convert.ChangeType(rnd.Next(min, max + 1), t);
                            }
                            else
                                throw new Exception("Invalid range definition at: " + val);
                        }
                        else
                        {
                            return Convert.ChangeType(val, t);
                        }
                    }
                }
            }
        }

        private static string GeneratedTextIsCorrect(string pattern)
        {
            Xeger sut = new Xeger(pattern, rnd);
            string result = sut.Generate();
            //string[] result = Enumerable.Range(1, 1).Select(i => sut.Generate()).ToArray();
            //Array.ForEach(result, regex => Assert.True(Regex.IsMatch(regex, pattern)));
            return result;
        }

        public static object CreateNewInstance(System.Type type)
        {
            object obj2 = new object();
            try
            {
                if (type.IsArray)
                {
                    return Array.CreateInstance(type.GetElementType(), 1);
                }
                if (type == typeof(string))
                {
                    return "";
                }
                if (type == typeof(Guid))
                {
                    return Guid.NewGuid();
                }
                if (type == typeof(XmlElement))
                {
                    XmlDocument document = new XmlDocument();
                    return document.CreateElement("MyElement");
                }
                if (type == typeof(XmlAttribute))
                {
                    XmlDocument document2 = new XmlDocument();
                    return document2.CreateAttribute("MyAttribute");
                }
                obj2 = Activator.CreateInstance(type);
            }
            catch
            {
            }
            return obj2;
        }
    }

    public class CustomTypeResolver : JavaScriptTypeResolver
    {
        public CustomTypeResolver() { }
        public override Type ResolveType(string id)
        {
            return System.Web.Compilation.BuildManager.GetType(id, false);
        }

        public override string ResolveTypeId(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            return type.Name;
        }
    }

}